import 'package:built_collection/built_collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:mobile_app.core/core/application/models/app_navigator_page_model.dart';
import 'package:mobile_app.core/core/application/models/app_navigator_transition_info.dart';
import 'package:mobile_app.core/core/presentation/widgets/app_navigator_page.dart';

class AppNavigatorTransitionDelegate extends TransitionDelegate<dynamic> {
  const AppNavigatorTransitionDelegate({
    required this.pageModels,
    required this.pageModelToTransitionInfo,
  });

  final BuiltList<AppNavigatorPageModel> pageModels;
  final BuiltMap<AppNavigatorPageModel, AppNavigatorTransitionInfo> pageModelToTransitionInfo;

  @override
  Iterable<RouteTransitionRecord> resolve({
    required List<RouteTransitionRecord> newPageRouteHistory,
    required Map<RouteTransitionRecord?, RouteTransitionRecord> locationToExitingPageRoute,
    required Map<RouteTransitionRecord?, List<RouteTransitionRecord>> pageRouteToPagelessRoutes,
  }) {
    final pageModelToExitingPageRoute = <AppNavigatorPageModel, RouteTransitionRecord>{};

    for (final exitingPageRoute in locationToExitingPageRoute.values) {
      final pageModel = _getPageRoutePageModel(exitingPageRoute);
      pageModelToExitingPageRoute[pageModel] = exitingPageRoute;
    }

    final pageModelToNewPageRoute = <AppNavigatorPageModel, RouteTransitionRecord>{};

    for (final newPageRoute in newPageRouteHistory) {
      final pageModel = _getPageRoutePageModel(newPageRoute);
      pageModelToNewPageRoute[pageModel] = newPageRoute;
    }

    final handledPageRoutes = <RouteTransitionRecord>[];

    for (final pageModel in pageModels) {
      final transitionInfo = pageModelToTransitionInfo[pageModel];
      final isLast = pageModels.last == pageModel;

      if (pageModelToNewPageRoute.containsKey(pageModel)) {
        final pageRoute = pageModelToNewPageRoute[pageModel]!;

        if (pageRoute.isWaitingForEnteringDecision) {
          final shouldBeMarkedForAdd =
              transitionInfo is AdditionNavigatorTransitionInfo && !transitionInfo.hasAnimation;

          if (!locationToExitingPageRoute.containsKey(pageRoute) &&
              !shouldBeMarkedForAdd &&
              isLast) {
            pageRoute.markForPush();
          } else {
            pageRoute.markForAdd();
          }
        }

        handledPageRoutes.add(pageRoute);
      } else if (pageModelToExitingPageRoute.containsKey(pageModel)) {
        final exitingPageRoute = pageModelToExitingPageRoute[pageModel]!;

        if (exitingPageRoute.isWaitingForExitingDecision) {
          final hasPagelessRoute = pageRouteToPagelessRoutes.containsKey(exitingPageRoute);

          final shouldBeMarkedForComplete =
              transitionInfo is RemovalNavigatorTransitionInfo && !transitionInfo.hasAnimation;

          if (isLast && !hasPagelessRoute && !shouldBeMarkedForComplete) {
            exitingPageRoute.markForPop(exitingPageRoute.route.currentResult);
          } else {
            exitingPageRoute.markForComplete(exitingPageRoute.route.currentResult);
          }

          if (hasPagelessRoute) {
            final pagelessRoutes = pageRouteToPagelessRoutes[exitingPageRoute]!;

            for (final pagelessRoute in pagelessRoutes) {
              if (pagelessRoute.isWaitingForExitingDecision) {
                if (isLast && pagelessRoute == pagelessRoutes.last && !shouldBeMarkedForComplete) {
                  pagelessRoute.markForPop(pagelessRoute.route.currentResult);
                } else {
                  pagelessRoute.markForComplete(pagelessRoute.route.currentResult);
                }
              }
            }
          }
        }

        handledPageRoutes.add(exitingPageRoute);
      }
    }

    final missedExitingPageRoutes = <RouteTransitionRecord>[];

    final handledPageModelSet = pageModels.toSet();

    var exitingPageRoute = locationToExitingPageRoute[null];

    while (exitingPageRoute != null) {
      if (exitingPageRoute.isWaitingForExitingDecision) {
        exitingPageRoute.markForComplete(exitingPageRoute.route.currentResult);

        final pagelessRoutes = pageRouteToPagelessRoutes[exitingPageRoute];

        if (pagelessRoutes != null) {
          for (final pagelessRoute in pagelessRoutes) {
            if (pagelessRoute.isWaitingForExitingDecision) {
              pagelessRoute.markForComplete(pagelessRoute.route.currentResult);
            }
          }
        }
      }

      final pageModel = _getPageRoutePageModel(exitingPageRoute);

      if (!handledPageModelSet.contains(pageModel)) {
        missedExitingPageRoutes.add(exitingPageRoute);
      }

      exitingPageRoute = locationToExitingPageRoute[exitingPageRoute];
    }

    if (missedExitingPageRoutes.isNotEmpty) {
      final missedPageModels = missedExitingPageRoutes.map(_getPageRoutePageModel);

      if (kDebugMode) {
        print('Navigator error! Missed exiting page models: $missedPageModels');
      }
    }

    final result = [
      ...missedExitingPageRoutes,
      ...handledPageRoutes,
    ];

    return result;
  }

  AppNavigatorPageModel _getPageRoutePageModel(RouteTransitionRecord pageRoute) {
    final page = pageRoute.route.settings as AppNavigatorPage;
    return page.pageModel;
  }
}
